package com.zishuimuyu.utills;

/**
 * TODO
 *
 * @author 紫水木鱼
 * @company 斯塔克工业
 * @date 2023年08月21日
 */

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.search.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author litong
 * @version 1.0
 * @date 2021/3/17 上午10:21
 * @description
 */
public class MailUtil {
    /**
     * <dependency>
     * <groupId>javax.mail</groupId>
     * <artifactId>mail</artifactId>
     * <version>1.4.5</version>
     * </dependency>
     * pop3和imap协议的不同
     * <p>
     * pop3:pop3允许电子邮件客户端请求下载指定邮件服务器上指定用户的邮件信息，
     * 但是在用户的客户端所做的任何操作都是不会反馈到服务器上的，也就是说，
     * 你已读了邮件在邮件服务器上的状态还是未读取的，
     * 这在很多情况下对用户来说是不方便的。这是因为pop3协议是单向协议
     * imap:IAMP协议，双向协议，用户在客户端的操作可以实时的反馈到服务器上，用户对邮件的任何操作，服务器也会做出相应的操作
     * 同时：IMAP还可以指定下载邮件的某些内容。
     * 所以POP3协议无法获取邮件状态(已读或未读)
     * IMAP协议不支持 中文主题 或  中文内容  邮件过滤,而POP3完全支持
     */
    public static void main(String[] args) throws Exception {
        pop3Receive("邮箱账号", "授权码");
        //收件
    }

    public static void pop3Receive(String username, String password) throws Exception {
        Folder folder = null;
        Store store = null;
        try {
            final String port = "993";
            // 服务器地址
            final String host = "pop.qq.com";
            final String protocol = "pop3";
            Properties props = new Properties();
            // 准备连接服务器的会话信息
            // 使用pop3协议
            props.setProperty("mail.store.protocol", protocol);
            props.setProperty("mmail.pop3.port", port);
            props.setProperty("mail.pop3.timeout", "60000");
            // 创建Session实例对象
            Session session = Session.getDefaultInstance(props);
            session.setDebug(false);
            store = session.getStore(protocol);
            store.connect(host, username, password);
            // 获得收件箱
            folder = store.getFolder("INBOX");
            //打开收件箱
            folder.open(Folder.READ_WRITE);
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, -2);
            Date mondayDate = calendar.getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date startTime = sdf.parse(sdf.format(mondayDate));
            System.out.println(startTime.getTime());
            Date endTime = new Date(startTime.getTime() + (1000 * 60 * 60 * 24 - 1));
            System.out.println(endTime.getTime());
            //添加筛选条件
            //ComparisonTerm类常用于日期和数字比较中，它使用六个常量EQ（＝）、GE（>=）、GT（>）、LE（<=）、LT（<）、NE（!=）来表示六种不同的比较操作。
            SearchTerm comparisonTermGe = new SentDateTerm(ComparisonTerm.GE, startTime);
            SearchTerm comparisonTermLe = new SentDateTerm(ComparisonTerm.LE, endTime);
            SearchTerm comparisonAndTerm = new AndTerm(comparisonTermGe, comparisonTermLe);
            SearchTerm subjectTerm = new SubjectTerm("Apple 提供的收据");
            SearchTerm andTerm = new AndTerm(new FromStringTerm("李大胖"), new BodyTerm("测试"));

            Message[] search = folder.search(andTerm);
            System.out.println("收件箱中共有:" + folder.getMessageCount() + "封邮件，搜索到" + search.length + "封符合条件的邮件!");
            if (search.length <= 0) {
                System.out.println("搜索过滤到" + search.length + " 封符合条件的邮件！");
                return;
            }
            // 获得收件箱中的邮件总数
            // false代表未读，true代表已读
            //FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
            for (Message message : search) {
                // 获得邮件主题
                String subject = message.getSubject();
                // 获得发送者地址
                Address from = (Address) message.getFrom()[0];
                System.out.println("邮件的主题为: " + subject);
                System.out.println("发件人地址为: " + getFrom(message));
                System.out.println("日期:" + getSentDate(message, null));
                message.getContent();
                System.out.println("----------------------allHeaders-----------------------------");
                StringBuffer txt = new StringBuffer();
                getMailTextContent(message, txt);
                //    String[] split = txt.toString().split("&&&&&&&&");
                System.out.println("邮件正文：" + txt.toString());
//                    System.out.println("邮件正文html: " +split[1]);
                boolean containAttachment = isContainAttachment(message);
                System.out.println("邮件中是否包含附件: " + containAttachment);
                if (containAttachment) {
                    String path = saveAttachment(message, "/home/litong/Desktop/");
                    System.out.println("文件保存地址: " + path);
                }
            }
        } finally {
            folder.close(true);
            store.close();
        }


    }

    public static void imapResceive(String username, String password) throws Exception {
        Folder folder = null;
        Store store = null;
        try {
            final String port = "993";
            // 服务器地址
            final String host = "imap.qq.com";
            final String protocol = "imap";
            Properties props = new Properties();
            // 准备连接服务器的会话信息
            // 使用pop3协议
            props.setProperty("mail.transport.protocol", protocol);
            props.setProperty("mail.stmp.port", port);
            props.setProperty("mail.stmp.timeout", "60000");
            props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            // 创建Session实例对象
            Session session = Session.getDefaultInstance(props);
            session.setDebug(false);
            store = session.getStore(protocol);
            store.connect(host, username, password);
            // 获得收件箱
            folder = store.getFolder("INBOX");
            //打开收件箱
            folder.open(Folder.READ_WRITE);
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, -2);
            Date mondayDate = calendar.getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date startTime = sdf.parse(sdf.format(mondayDate));
            System.out.println(startTime.getTime());
            Date endTime = new Date(startTime.getTime() + (1000 * 60 * 60 * 24 - 1));
            System.out.println(endTime.getTime());
            //添加筛选条件
            //ComparisonTerm类常用于日期和数字比较中，它使用六个常量EQ（＝）、GE（>=）、GT（>）、LE（<=）、LT（<）、NE（!=）来表示六种不同的比较操作。
            SearchTerm comparisonTermGe = new SentDateTerm(ComparisonTerm.GE, startTime);
            SearchTerm comparisonTermLe = new SentDateTerm(ComparisonTerm.LE, endTime);
            SearchTerm comparisonAndTerm = new AndTerm(comparisonTermGe, comparisonTermLe);
            SearchTerm subjectTerm = new SubjectTerm("Apple 提供的收据");
            SearchTerm andTerm = new AndTerm(new FromStringTerm("李大胖"), new BodyTerm("测试"));

            Message[] search = folder.search(comparisonAndTerm);
            System.out.println("收件箱中共有:" + folder.getMessageCount() + "封邮件，搜索到" + search.length + "封符合条件的邮件!");
            if (search.length <= 0) {
                System.out.println("搜索过滤到" + search.length + " 封符合条件的邮件！");
                return;
            }
            // 获得收件箱中的邮件总数
            // false代表未读，true代表已读
            //FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
            for (Message message : search) {
                // 获得邮件主题
                String subject = message.getSubject();
                // 获得发送者地址
                Address from = (Address) message.getFrom()[0];
                System.out.println("邮件的主题为: " + subject);
                System.out.println("发件人地址为: " + getFrom(message));
                System.out.println("日期:" + getSentDate(message, null));
                message.getContent();
                System.out.println("----------------------allHeaders-----------------------------");
                StringBuffer txt = new StringBuffer();
                getMailTextContent(message, txt);
                //    String[] split = txt.toString().split("&&&&&&&&");
                System.out.println("邮件正文：" + txt.toString());
//                    System.out.println("邮件正文html: " +split[1]);
                boolean containAttachment = isContainAttachment(message);
                System.out.println("邮件中是否包含附件: " + containAttachment);
                if (containAttachment) {
                    String path = saveAttachment(message, "/home/litong/Desktop/");
                    System.out.println("文件保存地址: " + path);
                }
            }
        } finally {
            folder.close(true);
            store.close();
        }


    }

    /**
     * 获得邮件发件人
     *
     * @param msg 邮件内容
     * @return 姓名 <Email地址>
     * @throws MessagingException
     * @throws UnsupportedEncodingException
     */
    public static String getFrom(Message msg) throws MessagingException, UnsupportedEncodingException {
        String from = "";
        Address[] froms = msg.getFrom();
        if (froms.length < 1){
            throw new MessagingException("没有发件人!");
        }

        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();
        if (person != null) {
            person = MimeUtility.decodeText(person) + " ";
        } else {
            person = "";
        }
        from = person.trim() + "|" + address.getAddress();

        return from;
    }

    /**
     * 获得邮件发送时间
     *
     * @param msg 邮件内容
     * @return yyyy年mm月dd日 星期X HH:mm
     * @throws MessagingException
     */
    public static String getSentDate(Message msg, String pattern) throws MessagingException {
        Date receivedDate = msg.getSentDate();
        if (receivedDate == null){
            return "";
        }

        if (pattern == null || "".equals(pattern)){
            pattern = "yyyy年MM月dd日 E HH:mm ";
        }

        return new SimpleDateFormat(pattern).format(receivedDate);
    }


    /**
     * 文本解码
     *
     * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本
     * @return 解码后的文本
     * @throws UnsupportedEncodingException
     */
    public static String decodeText(String encodeText) throws UnsupportedEncodingException {
        if (encodeText == null || "".equals(encodeText)) {
            return "";
        } else {
            return MimeUtility.decodeText(encodeText);
        }
    }

    /**
     * 判断邮件中是否包含附件
     *
     * @param part 邮件内容
     * @return 邮件中存在附件返回true，不存在返回false
     * @throws MessagingException
     * @throws IOException
     */
    public static boolean isContainAttachment(Part part) throws MessagingException, IOException {
        boolean flag = false;
        if (part.isMimeType("multipart/*")) {
            MimeMultipart multipart = (MimeMultipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    flag = true;
                } else if (bodyPart.isMimeType("multipart/*")) {
                    flag = isContainAttachment(bodyPart);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.indexOf("application") != -1) {
                        flag = true;
                    }

                    if (contentType.indexOf("name") != -1) {
                        flag = true;
                    }
                }

                if (flag){
                    break;
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            flag = isContainAttachment((Part) part.getContent());
        }
        return flag;
    }

    /**
     * 获得邮件文本内容
     *
     * @param part    邮件体
     * @param content 存储邮件文本内容的字符串
     * @throws MessagingException
     * @throws IOException
     */
    public static void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
        //如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
        boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
        if (part.isMimeType("text/*") && !isContainTextAttach) {
            content.append(part.getContent().toString()).append("&&&&&&&&");
        } else if (part.isMimeType("message/rfc822")) {
            getMailTextContent((Part) part.getContent(), content);
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                getMailTextContent(bodyPart, content);
            }
        }
    }

    /**
     * 保存附件
     *
     * @param part    邮件中多个组合体中的其中一个组合体
     * @param destDir 附件保存目录
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static String saveAttachment(Part part, String destDir) throws MessagingException, IOException {
        String path = "";
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();    //复杂体邮件
            //复杂体邮件包含多个邮件体
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                //获得复杂体邮件中其中一个邮件体
                BodyPart bodyPart = multipart.getBodyPart(i);
                //某一个邮件体也有可能是由多个邮件体组成的复杂体
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    InputStream is = bodyPart.getInputStream();
                    path = saveFile(is, destDir, decodeText(bodyPart.getFileName()));
                } else if (bodyPart.isMimeType("multipart/*")) {
                    saveAttachment(bodyPart, destDir);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.indexOf("name") != -1 || contentType.indexOf("application") != -1) {
                        path = saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            saveAttachment((Part) part.getContent(), destDir);
        }
        return path;
    }

    /**
     * 读取输入流中的数据保存至指定目录
     *
     * @param is       输入流
     * @param fileName 文件名
     * @param destDir  文件存储目录
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static String saveFile(InputStream is, String destDir, String fileName) throws FileNotFoundException, IOException {
        String path = destDir.concat(fileName);
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(new File(path)));
        int len = -1;
        while ((len = bis.read()) != -1) {
            bos.write(len);
            bos.flush();
        }
        bos.close();
        bis.close();
        return path;
    }


}
